package agile.tools.autotest;

import java.util.ArrayList;
import java.util.List;

import org.aspectj.lang.JoinPoint;

import agile.tools.autotest.persister.Persister;
import agile.tools.autotest.persister.impl.LocalFileSystemPersister;
import agile.tools.autotest.serializer.Serializer;
import agile.tools.autotest.serializer.impl.FlexJsonSerializer;

public class MethodRecorder {
	private Serializer serializer = new FlexJsonSerializer();
	private Persister persister = new LocalFileSystemPersister();
	MethodRecord record = new MethodRecord();

	private MethodRecorder() {
	}

	public static MethodRecorder defaultMethodRecorder() {
		MethodRecorder recorder = new MethodRecorder();
		recorder.serializer = new FlexJsonSerializer();
		recorder.persister = new LocalFileSystemPersister();
		return recorder;
	}

	public static MethodRecorder methodRecorderFor(Serializer serializer, Persister persister) {
		MethodRecorder recorder = new MethodRecorder();
		recorder.serializer = serializer;
		recorder.persister = persister;
		return recorder;
	}

	public static MethodRecorder methodRecorderFor(Serializer serializer) {
		MethodRecorder recorder = new MethodRecorder();
		recorder.serializer = serializer;
		recorder.persister = new LocalFileSystemPersister();
		return recorder;
	}

	public static MethodRecorder methodRecorderFor(Persister persister) {
		MethodRecorder recorder = new MethodRecorder();
		recorder.serializer = new FlexJsonSerializer();
		recorder.persister = persister;
		return recorder;
	}

	public void record(String className, String methodName, Object[] inputParams) {
		this.recordObjectUnderTest(className);
		this.recordMethodName(methodName);
		this.recordInputParams(inputParams);
	}

	public void record(JoinPoint jp) {
		this.recordObjectUnderTest(jp.getStaticPart().getSignature().getDeclaringTypeName());
		this.recordMethodName(jp.getSignature().getName());
		this.recordInputParams(jp.getArgs());
	}

	public void recordObjectUnderTest(String className) {
		record.objectUnderTest(className);
	}

	public void recordMethodName(String methodName) {
		record.methodName(methodName);
	}

	public void recordInputParams(Object[] inputParams) {
		for (Object obj : inputParams) {
			record.serializedInputParam(serializer.serializeAsString(obj), obj.getClass().getName());
		}
	}

	public Object recordReturnValue(Object returnObj) {
		record.serializedReturnValue(serializer.serializeAsString(returnObj));
		if(returnObj != null)
			record.returnValueType(returnObj.getClass().getName());
		return returnObj;
	}

	public Object recordReturnValueAndPersist(Object returnObj) {
		Object obj = recordReturnValue(returnObj);
		persister.persist(record);
		return obj;
	}

	public Object deserializedExpectedReturn() {
		return serializer.deSerialize(record.serializedReturnValue());
	}

	public List<Object> deserializedInputParams() {
		List<Object> inputParams = new ArrayList<Object>();
		for (Object obj : record.serializedInputParams()) {
			inputParams.add(serializer.deSerialize(obj));
		}
		return inputParams;
	}

	@SuppressWarnings("rawtypes")
	public Class[] deserializedInputParamsTypes() {
		List<Object> deserializedInputParams = deserializedInputParams();
		Class[] classes = new Class[deserializedInputParams().size()];
		for (int i = 0; i < deserializedInputParams.size(); i++) {
			classes[i] = deserializedInputParams.get(i).getClass();
		}
		return classes;
	}
	
	public Class[] deserializedInputParamsTypesNullIfEmpty() {
		Class[] inputParamClasses = deserializedInputParamsTypes();
		if(inputParamClasses.length == 0)
			return null;
		return inputParamClasses;
	}
	
	public MethodRecord getSingleTestDataSet(String forDataSetReference) {
		this.record = persister.getSingleTestDataSet(forDataSetReference);
		return record;
	}

	public String objectUnderTest() {
		return record.objectUnderTest();
	}

	public String methodUnderTest() {
		return record.methodName();
	}

	public Persister persister() {
		return persister;
	}

	public Serializer serializer() {
		return serializer;
	}
}